#include "sem2.h"

#include "thread.h"
#include "list.h"

#include <stdlib.h>
#include <stdio.h>

struct thread_sem {
  int lock;
  unsigned int value;
  list * thread_queue;
};

int thread_sem_init(thread_sem_t **sem, unsigned int value)
{
  *sem = malloc(sizeof(thread_sem_t));
  (*sem)->lock= 0;
  (*sem)->value = value;
  (*sem)->thread_queue = list_new();

  return 0;
}

int thread_sem_wait(thread_sem_t *sem)
{
  while (1)
  {
    while (__sync_val_compare_and_swap(&(sem->lock), 0, 1) == 1); // attend de pouvoir regarder le sémaphore
    if (sem->value == 0)
    {
      __sync_and_and_fetch(&(sem->lock), 0);
      /* On doit endormir le thread */
      if (list_find(sleep_queue, thread_current) != NULL)
      {
        fprintf(stderr, "Error thread_sem_wait: thread already in the sleep_queue\n");
      }
      if (list_find(sem->thread_queue, thread_current) != NULL)
      {
        fprintf(stderr, "Error thread_sem_wait: thread already in the queue\n");
      }
        // On ajoute le thread dans la queue d'attente
      sem->thread_queue = list_add(sem->thread_queue, thread_current);
      // On endort le thread
      sleep_queue = list_add(sleep_queue, thread_current);
      // On lance le thread suivant
      thread_next();
    }
    else
    {
      (sem->value)--;
      __sync_and_and_fetch(&(sem->lock), 0);
      break; // on a pris le sémaphore
    }
  }

  return 0;
}

int thread_sem_post(thread_sem_t *sem)
{
  __sync_fetch_and_add(&(sem->value), 1);

  // On réveille un thread en attente, s'il y en a
  if (!list_isempty(sem->thread_queue))
  {
    thread_t thread_waiting = sem->thread_queue->first->data;
    thread_queue = list_add(thread_queue, thread_waiting);
    sem->thread_queue = list_remove(sem->thread_queue, thread_waiting);
    sleep_queue = list_remove(sleep_queue, thread_waiting);
    thread_next();
  }

  return 0;
}

int thread_sem_getvalue(thread_sem_t *sem, int * sval)
{
  *sval = sem->value;

  return 0;
}

int thread_sem_destroy(thread_sem_t *sem)
{
  list_free(sem->thread_queue);
  free(sem);

  return 0;
}
